package com.karry.yl.service.financial.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.karry.commons.Constants;
import com.karry.commons.base.impl.SuperServiceImpl;
import com.karry.yl.dao.financial.ReceiptItemsMapper;
import com.karry.yl.dao.financial.ReceiptMapper;
import com.karry.yl.dao.services.RecordsMapper;
import com.karry.yl.domain.financial.Receipt;
import com.karry.yl.domain.financial.ReceiptItems;
import com.karry.yl.service.financial.ReceiptService;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.ponly.payment.util.MD5;
import org.ponly.payment.util.SignUtils;
import org.ponly.payment.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

@Service
public class ReceiptServiceImpl extends SuperServiceImpl<ReceiptMapper,Receipt> implements ReceiptService {
    private static final Logger log = LoggerFactory.getLogger(ReceiptServiceImpl.class);
    @Autowired
    private ReceiptItemsMapper receiptItemsMapper;
    @Autowired
    private RecordsMapper recordsMapper;

    @Override
    @Transactional
    public void save(Receipt receipt, List<ReceiptItems> items) {
        String code = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        receipt.setCode(code);
        baseMapper.insert(receipt);
        if(null != items) {
            for(ReceiptItems item : items){
                recordsMapper.updateStatusTo1(item.getRecordsId());
                item.setReceiptId(receipt.getId());
                receiptItemsMapper.insert(item);
            }
        }

    }

    @Override
    @Transactional
    public void update(Receipt receipt, List<ReceiptItems> items) {
        Receipt receiptOld = baseMapper.selectById(receipt.getId());
        receiptOld.setEndTime(receipt.getEndTime());
        receiptOld.setAmount(receipt.getAmount());
        receiptOld.setPaymentMethod(receipt.getPaymentMethod());
        String code = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        receiptOld.setCode(code);
        baseMapper.updateById(receiptOld);
        //恢复该收款项目对应的服务记录的状态为 为 未关联账单
        List<Long> recordsIds = receiptItemsMapper.selectIdsByReceiptId(receipt.getId());
        for(Long id :recordsIds){
            recordsMapper.updateStatusTo0(id);
        }
        //重新添加收费项目
        if(null != items && items.size() > 0){
            //删除已有的收款项目
            receiptItemsMapper.delete(new EntityWrapper<ReceiptItems>().eq("receiptId",receipt.getId()));
            for(ReceiptItems item : items){
                recordsMapper.updateStatusTo1(item.getRecordsId());
                item.setReceiptId(receipt.getId());
                receiptItemsMapper.insert(item);
            }
        }
    }

    @Override
    @Transactional
    public String updateCode(String oldCode) {
        Receipt receipt = this.selectOne(new EntityWrapper<Receipt>().eq("code",oldCode));
        String code = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        receipt.setCode(code);
        baseMapper.updateById(receipt);
        return code;
    }

    @Override
    @Transactional
    public void delete(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);
        //删除之前需要将 已经关联到收费单的服务项目 状态修改回去
        for(Long id : ids){
            List<Long> recordsIds = receiptItemsMapper.selectIdsByReceiptId(id);
            for(Long recordsId :recordsIds){
                recordsMapper.updateStatusTo0(recordsId);
            }
            receiptItemsMapper.delete(new EntityWrapper<ReceiptItems>().eq("receiptId",id));
        }
    }

    @Override
    public void updateStatusForPayment(String code, BigDecimal realAmount,int paymentMethod) {
       baseMapper.updateStatusForPayment(code,realAmount,paymentMethod);

    }

    @Override
    public boolean isPaid(String code) {
        int i = baseMapper.selectByStatus(Receipt.STATUS_YZF, code);
        return i > 0;
    }

    @Override
    public Map<String, String> pay(Map<String, String> paramsMap) {
        paramsMap.put("notify_url", "https://www.kerry365.com/receipt/pay/notify");
        paramsMap.put("version", Constants.wft_version);
        paramsMap.put("charset", Constants.wft_charset);
        paramsMap.put("sign_type", Constants.wft_sign_type);
        paramsMap.put("mch_create_ip",Constants.IP);
        paramsMap.put("nonce_str", String.valueOf(new Date().getTime()));
        Map<String, String> params = SignUtils.paraFilter(paramsMap);
        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
        SignUtils.buildPayParams(buf, params, false);
        String preStr = buf.toString();
        String sign = MD5.sign(preStr, "&key=" + paramsMap.get("key"), "utf-8");
        paramsMap.put("sign", sign);
        String reqUrl = Constants.wft_req_url;
        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        Map<String, String> resultMap = null;
        try {
            HttpPost httpPost = new HttpPost(reqUrl);
            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(paramsMap), "utf-8");
            httpPost.setEntity(entityParams);
            httpPost.setHeader("Content-Type", "text/xml;utf-8");
            client = HttpClients.createDefault();
            response = client.execute(httpPost);
            if(response != null && response.getEntity() != null){
                resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
                if(resultMap.containsKey("sign") && !SignUtils.checkParam(resultMap,  paramsMap.get("key"))){
                    log.error("验证签名不通过");
                    resultMap = null;
                }else {
                    resultMap.put("out_trade_no",paramsMap.get("out_trade_no"));
                    if(resultMap.containsKey("err_code") && (resultMap.get("err_code").equals("Order exists") || resultMap.get("err_code").equals("up.duplicate.out_trade_no") )){  //如果订单号重复则关闭旧的订单，重新发起支付订单
                        this.closeTrade(paramsMap.get("out_trade_no"),paramsMap.get("key")); // 关闭旧的支付订单
                        String out_trade_no = this.updateCode(paramsMap.get("out_trade_no"));
                        paramsMap.put("out_trade_no",out_trade_no);
                        return this.pay(paramsMap); //重新发起支付
                    }
                }
            }
        } catch (Exception e) {
            log.error("操作失败，原因：",e);
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return resultMap;
    }


    @Override
    public void closeTrade(String code, String key) {
        Map<String,String> paramsMap = new HashMap<>();
        paramsMap.put("service", "unified.trade.close");
        paramsMap.put("version", Constants.wft_version);
        paramsMap.put("charset", Constants.wft_charset);
        paramsMap.put("sign_type", Constants.wft_sign_type);
        paramsMap.put("mch_id", "029116000001");  //商户ID
        paramsMap.put("nonce_str", String.valueOf(new Date().getTime()));
        Map<String, String> params = SignUtils.paraFilter(paramsMap);
        StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
        SignUtils.buildPayParams(buf, params, false);
        String preStr = buf.toString();
        String sign = MD5.sign(preStr, "&key=" + key, "utf-8");
        paramsMap.put("sign", sign);

        String reqUrl = Constants.wft_req_url;

        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        Map<String, String> resultMap = null;
        try {
            HttpPost httpPost = new HttpPost(reqUrl);
            StringEntity entityParams = new StringEntity(XmlUtils.parseXML(paramsMap), "utf-8");
            httpPost.setEntity(entityParams);
            httpPost.setHeader("Content-Type", "text/xml;utf-8");
            client = HttpClients.createDefault();
            response = client.execute(httpPost);
        } catch (Exception e) {
            log.error("关单失败，原因：",e);
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
